package com.miyu.common.utils.redis;

import com.alibaba.fastjson.parser.ParserConfig;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.miyu.common.utils.redis.redisUtils.entity.RedisEntity;
import com.miyu.common.utils.redis.redisUtils.props.JsonSerialType;
import com.miyu.common.utils.redis.redisUtils.serializer.CustomFastJsonRedisSerializer;
import com.miyu.common.utils.redis.redisUtils.serializer.CustomStringRedisSerializer;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.context.properties.bind.Binder;
import org.springframework.context.EnvironmentAware;
import org.springframework.core.env.Environment;
import org.springframework.data.redis.cache.RedisCache;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.serializer.*;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.util.NoSuchElementException;

/**
 * 处理redis注解空间以及名称处理 增加失效时间
 * 名称+#+300 时间格式 默认单位为秒
 * @author longmy
 */
@Slf4j
public class CacheRedisCacheManager extends RedisCacheManager implements EnvironmentAware {
    public CacheRedisCacheManager(RedisCacheWriter cacheWriter, RedisCacheConfiguration defaultCacheConfiguration) {
        super(cacheWriter, defaultCacheConfiguration);
    }


    private Environment environment;
    private Binder binder;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
        this.binder = Binder.get(this.environment);
    }
    /**
     * 创建复述,缓存
     *
     * @param name        名字
     * @param cacheConfig 缓存配置
     * @return {@link RedisCache}
     */
    @Override
    protected RedisCache createRedisCache(String name, RedisCacheConfiguration cacheConfig) {
        String[] cells = StringUtils.delimitedListToStringArray(name, "#");
        name = cells[0];
        String finalName = name;

        RedisEntity redisEntity = new RedisEntity();
        try {
            redisEntity = binder.bind("spring.redis", RedisEntity.class).get();
        } catch (NoSuchElementException e) {
            log.error("Failed to configure knife4j redis: 'knife4j.redis' attribute is not specified and no embedded redis could be configured.");

        }
        JsonSerialType jsonSerialType = redisEntity.getJsonSerialType();
        RedisSerializer stringRedisSerializer = null;
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = null;
        if(jsonSerialType != null && jsonSerialType == JsonSerialType.Fastjson){
            //若配置，则采用该方式
            jackson2JsonRedisSerializer = new CustomFastJsonRedisSerializer<>(Object.class);
            ParserConfig.getGlobalInstance().setAutoTypeSupport(false);
            stringRedisSerializer = new CustomStringRedisSerializer();
        } else {
            // 内置默认序列化(此处若不设置则采用默认的JDK设置，也可以在使用使自定义序列化方式)
            jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
            ObjectMapper om = new ObjectMapper();
            om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
            om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
            jackson2JsonRedisSerializer.setObjectMapper(om);
            stringRedisSerializer = new StringRedisSerializer();
        }
        //设置失效时间#以及命名key设置
        if (cells.length > 1) {
            long ttl = Long.parseLong(cells[1]);
            /*根据传参设置缓存失效时间*/
            cacheConfig = cacheConfig.entryTtl(Duration.ofSeconds(ttl))
                    .computePrefixWith(cacheName -> finalName+":")
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                    .disableCachingNullValues();
        }else{
            cacheConfig = cacheConfig
                    .computePrefixWith(cacheName -> finalName +":")
                    .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(stringRedisSerializer))
                    .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer))
                    .disableCachingNullValues();
        }
        return super.createRedisCache(name, cacheConfig);
    }
}
